﻿using System;
using System.Collections;
using System.Collections.Specialized;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Security.Cryptography;//这个是处理文字编码的前提


namespace EasyChat_Server
{

    class Listener
    {
        private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
        string encode;
        string decode;
        string _descode;
        private static string readpublickey = "<RSAKeyValue><Modulus>0OLNSBeCcxsQ7gaPFUI6M36KWQ9PiRl2MF7tx3qw9YxWkvmy8pID7bjGf9vHEbYMv9D2K3pePTkeSGQ3pt4W0tocKiMFomb8fkoNUsVoS/+xBgsE0Le2FmoUnJBhBu7o7Mi+Ue0Fnr8NshcNVINSpNTSdnTapeEosmSfKhjetpE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
        private static string readprivatekey = "<RSAKeyValue><Modulus>0OLNSBeCcxsQ7gaPFUI6M36KWQ9PiRl2MF7tx3qw9YxWkvmy8pID7bjGf9vHEbYMv9D2K3pePTkeSGQ3pt4W0tocKiMFomb8fkoNUsVoS/+xBgsE0Le2FmoUnJBhBu7o7Mi+Ue0Fnr8NshcNVINSpNTSdnTapeEosmSfKhjetpE=</Modulus><Exponent>AQAB</Exponent><P>9mqXKcmJF5RnSAM7GQTQf9L2RujyZb8Q4LgtSv11vhadwodJPXkGk0gir6P13aA91e4tue8k4dw10UZ8xUfNaw==</P><Q>2QKLicWhFr+VxsPOuZbm5ZrZT5IK7mUV9P9zXgJfk7NkuDlL0lbQyE/eIfSqQQ1aHuUO18kwAMoLgnGVRkiu8w==</Q><DP>YCVY09op3oRuBAAWt/1T6PwEUcikg6SYTkYujfr1TPUAGx20KwMk2KSlvAhqb04NsoIbX1f6+RF2AjDRkBwi/w==</DP><DQ>NnC+AsUSgXt0npWWsdZafHVZWO3hUicH2BuRnJ7pzUru1IyDKfRfkjD/H/OaFpG77w41qAqvwTLuqj3GXghz4w==</DQ><InverseQ>1dYl6cdguE1VgUX56eh6eTfGjXia5DcHRj9E6gd3ZjGcpW0PDX2dDFbkeR78fBVynuNgStfE6jaaqFXxPojkLA==</InverseQ><D>FnOKJOmB9+U82JiT4HER2F8Nd60Rm2bFMUXCwOILCxleZMW52iCfk3l8vFle+CRdEnDfRKUOY/4zpww+fwz33tgLH/12+kz8r4czcrvGCWLbPosgFTxqfxmvNNQVukNtAzoGv7n3ActK5xbUCF+Y7d1VjV9AXPw+APEUJZ2oftE=</D></RSAKeyValue>";
        private static RSACryptoServiceProvider crypt;
        byte[] bytes;

        /// <summary> 
        /// DES加密字符串 
        /// </summary> 
        /// <param name="encryptString">待加密的字符串</param> 
        /// <param name="encryptKey">加密密钥,要求为8位</param> 
        /// <returns>加密成功返回加密后的字符串，失败返回源串</returns> 
        public static string EncryptDES(string encryptString, string encryptKey)
        {
            try
            {
                byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8));
                byte[] rgbIV = Keys;
                byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString);
                DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                cStream.Write(inputByteArray, 0, inputByteArray.Length);
                cStream.FlushFinalBlock();
                return Convert.ToBase64String(mStream.ToArray());
            }
            catch
            {
                return encryptString;
            }
        }
        /// <summary> 
        /// DES解密字符串 
        /// </summary> 
        /// <param name="decryptString">待解密的字符串</param> 
        /// <param name="decryptKey">解密密钥,要求为8位,和加密密钥相同</param> 
        /// <returns>解密成功返回解密后的字符串，失败返源串</returns> 
        public static string DecryptDES(string decryptString, string decryptKey)
        {
            try
            {
                byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey);
                byte[] rgbIV = Keys;
                byte[] inputByteArray = Convert.FromBase64String(decryptString);
                DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider();
                MemoryStream mStream = new MemoryStream();
                CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
                cStream.Write(inputByteArray, 0, inputByteArray.Length);
                cStream.FlushFinalBlock();
                return Encoding.UTF8.GetString(mStream.ToArray());
            }
            catch
            {
                return decryptString;
            }
        }
        #region 字段定义

        /// <summary>
        /// 服务器程序使用的端口，默认为8888
        /// </summary>
        private int _port = 8888;

        /// <summary>
        /// 接收数据缓冲区大小64K 
        /// </summary>
        private const int _maxPacket = 64 * 1024;

        /// <summary>
        /// 服务器端的监听器
        /// </summary>
        private TcpListener _tcpl = null;

        /// <summary>
        /// 保存所有客户端会话的哈希表
        /// </summary>
        private Hashtable _transmit_tb = new Hashtable();
        private Hashtable _code_tb = new Hashtable();

        #endregion
        

        #region 服务器方法

        /// <summary>
        /// 关闭监听器并释放资源
        /// </summary>
        public void Close()
        {
            if (_tcpl != null)
            {
                _tcpl.Stop();//如果不为空 就停止 tcplistener
            }
            //关闭客户端连接并清理资源
            if (_transmit_tb.Count != 0)//客户端会话的哈希表
            {
                foreach (Socket session in _transmit_tb.Values)
                {
                    session.Shutdown(SocketShutdown.Both);
                }
                _transmit_tb.Clear();
                _transmit_tb = null;
            }
        }



        /// <summary>
        /// 配置监听端口号 
        /// </summary>
        public void GetConfig()
        {
            string portParam;
            Console.Write("请输入监听端口，直接回车则接受默认端口8888: ");
            //portParam = Console.ReadLine();
            portParam = Console.ReadLine();

            if (portParam != string.Empty)
            {
                if (!int.TryParse(portParam, out _port) || _port < 1023 || _port > 65535)//很是精炼的语句
                {
                    _port = 8888;
                    Console.WriteLine("端口号不合法,默认端口号被接受!");
                }
            }
        }

        /// <summary>
        /// 序列化在线列表，向客户端返回序列化后的字节数组
        /// </summary>
        /// <returns>序列化后的字节数组</returns>
        private byte[] SerializeOnlineList()
        {
            StringCollection onlineList = new StringCollection();
            foreach (object o in _transmit_tb.Keys)
            {
                onlineList.Add(o as string);//转换语句
            }
            IFormatter format = new BinaryFormatter();//以二进制格式将对象或整个连接对

                                                      //象图形序列化和反序列化。
            MemoryStream stream = new MemoryStream();
            format.Serialize(stream, onlineList);//保持到内存流
            byte[] ret = stream.ToArray();
            stream.Close();
            return ret;
        }

        /// <summary>
        /// 提取命令
        /// 格式为两个一位整数拼接成的字符串。
        /// 第一位为0表示客户机向服务器发送的命令，为1表示服务器向客户机发送的命令。
        /// 第二位表示命令的含义，具体如下：
        /// "01"-离线
        /// "02"-请求在线列表
        /// "03"-请求对所有人闪屏振动
        /// "04"-请求对指定用户闪屏振动
        /// "05"-请求广播消息
        /// default-转发给指定用户
        /// </summary>
        /// <param name="s">要解析的包含命令的byte数组，只提取前两个字节</param>
        /// <returns>拼接成的命令</returns>
        private string DecodingBytes(byte[] s)
        {
            return string.Concat(s[0].ToString(), s[1].ToString());
        }

        /// <summary>
        /// 线程执行体,转发消息-------------------
        /// </summary>
        /// <param name="obj">传递给线程执行体的用户名，用以与用户通信</param>
        private void ThreadFunc(object obj)
        {
            //通过转发表得到当前用户套接字
            Socket clientSkt = _transmit_tb[obj] as Socket;
            
            while (true)
            {
                try
                {
                    //接受第一个数据包。
                    //仅有两种情况，一为可以识别的命令格式，否则为要求转发给指定用户的用户名。
                    //这里的实现不够优雅，但不失为此简单模型的一个解决之道。
                    byte[] _cmdBuff = new byte[128];
                    clientSkt.Receive(_cmdBuff);
                    string _str = Encoding.Default.GetString(_cmdBuff).TrimEnd('\0');
                   
                    _descode = _code_tb[obj] as string;
                    decode = DecryptDES(_str, _descode);
                   
                    byte[] byte_cmdBuff = Encoding.Default.GetBytes(decode);

                    /// "01"-离线
                    /// "02"-请求在线列表
                    /// "03"-请求对所有人闪屏振动
                    /// "04"-请求对指定用户闪屏振动
                    /// "05"-请求广播消息
                    /// default-转发给指定用户
                    switch (decode)
                    {
                        case "01":
                            {
                                _descode = _code_tb[obj] as string;
                                _transmit_tb.Remove(obj);
                                _code_tb.Remove(obj);
                                string svrlog = string.Format("[消息日志] {0} ：用户 {1} 已断开... 当前在线人数: {2}\r\n", DateTime.Now, obj, _transmit_tb.Count);
                                Console.WriteLine(svrlog);

                                //向所有客户机发送系统消息
                                foreach (DictionaryEntry de in _transmit_tb)
                                {
                                    string _clientName = de.Key as string;
                                    Socket _clientSkt = de.Value as Socket;

                                    _descode = _code_tb[_clientName] as string;
                                    encode = EncryptDES(svrlog, _descode);
                                    byte_cmdBuff = Encoding.Unicode.GetBytes(encode);
                                    _clientSkt.Send(byte_cmdBuff);//给其他用户通知现在在线的用户
                                    //_clientSkt.Send(Encoding.Unicode.GetBytes(svrlog));
                                }
                                Thread.CurrentThread.Abort();
                                break;
                            }
                        case "02":
                            {
                                _descode = _code_tb[obj] as string;
                                byte[] onlineBuff = SerializeOnlineList();
                                //先发送响应信号，用于客户机的判断，"11"表示服务发给客户机的更新在线列表的命令
                                _descode = _code_tb[obj] as string;
                                encode = EncryptDES("11", _descode);
                                byte_cmdBuff = Encoding.Unicode.GetBytes(encode);
                                clientSkt.Send(byte_cmdBuff);
                                clientSkt.Send(onlineBuff);
                                break;
                            }
                        case "03":
                            {
                                string svrlog = string.Format("[消息日志] {0} ：用户 {1} 群发闪屏。\r\n",DateTime.Now, obj);
                                Console.WriteLine(svrlog);
                                string displayTxt = string.Format("[系统提示]用户 {0} 向您发送了一个闪屏振动。\r\n\r\n", obj);
                                foreach (DictionaryEntry de in _transmit_tb)
                                {
                                    string _clientName = de.Key as string;
                                    
                                    Socket _clientSkt = de.Value as Socket;
                                    
                                    
                                    if (!_clientName.Equals(obj))
                                    {
                                        //先发送响应信号，用于客户机的判断，"12"表示服务发给客户机的闪屏振动的命令

                                        _descode = _code_tb[_clientName] as string;
                                        encode = EncryptDES("12", _descode);
                                        byte_cmdBuff = Encoding.Unicode.GetBytes(encode);
                                        _clientSkt.Send(byte_cmdBuff);


                                        encode = EncryptDES(displayTxt, _descode);
                                        byte_cmdBuff = Encoding.Unicode.GetBytes(encode);
                                        _clientSkt.Send(byte_cmdBuff);
                                    }
                                }
                                break;
                            }
                        case "04":
                            {
                                
                                string _receiver = null;
                                
                                byte[] _receiverBuff = new byte[128];
                                clientSkt.Receive(_receiverBuff);
                               string _rectmp = Encoding.Unicode.GetString(_receiverBuff).TrimEnd('\0');

                               _descode = _code_tb[obj] as string;
                               _receiver = DecryptDES(_rectmp, _descode);
                               string svrlog = string.Format("[消息日志] {0} ：用户 {1} 向用户 {2} 发送闪屏。\r\n", DateTime.Now, obj, _receiver);
                               Console.WriteLine(svrlog);

                                string displayTxt = string.Format("[系统提示]用户 {0} 向您发送了一个闪屏振动。\r\n\r\n", obj);
                                //通过转发表查找接收方的套接字
                                if (_transmit_tb.ContainsKey(_receiver))
                                {
                                    Socket receiverSkt = _transmit_tb[_receiver] as Socket;
                                    _descode = _code_tb[_receiver] as string;
                                    
                                    encode = EncryptDES("12", _descode);
                                    byte_cmdBuff = Encoding.Unicode.GetBytes(encode);
                                    receiverSkt.Send(byte_cmdBuff);
                                    //receiverSkt.Send(new byte[] { 1, 2 });
                                    encode = EncryptDES(displayTxt, _descode);
                                    byte_cmdBuff = Encoding.Unicode.GetBytes(encode);
                                    receiverSkt.Send(byte_cmdBuff);
                                    //receiverSkt.Send(Encoding.Unicode.GetBytes(displayTxt));
                                }
                                else
                                {
                                    string sysMessage = string.Format("[系统消息]您刚才的闪屏振动没有发送成功。\r\n可能原因：用户 {0} 已离线或者网络阻塞。\r\n", _receiver);
                                    svrlog = string.Format("[系统消息]{0}：用户 {1} 闪屏振动没有发送成功。\r\n可能原因：用户 {2} 已离线或者网络阻塞。\r\n", DateTime.Now, obj, _receiver);
                                    Console.WriteLine(svrlog);
                                    encode = EncryptDES(sysMessage, _descode);
                                    byte_cmdBuff = Encoding.Unicode.GetBytes(encode);
                                    clientSkt.Send(byte_cmdBuff);
                                    //clientSkt.Send(Encoding.Unicode.GetBytes(sysMessage));
                                }
                                break;
                            }
                        case "05":
                            {
                                _descode = _code_tb[obj] as string;
                                byte[] _msgBuff = new byte[_maxPacket];
                                clientSkt.Receive(_msgBuff);

                                string _strMsgBuff = Encoding.Unicode.GetString(_msgBuff).TrimEnd('\0');
                                decode = DecryptDES(_strMsgBuff, _descode);
                                string svrlog = string.Format("[消息日志] {0} ：用户 {1} 群发消息。\r\n", DateTime.Now, obj);
                                Console.WriteLine(svrlog);
                                foreach (DictionaryEntry de in _transmit_tb)
                                {
                                    string _clientName = de.Key as string;
                                    Socket _clientSkt = de.Value as Socket;
                                    _descode = _code_tb[_clientName] as string;
                                    encode=EncryptDES(decode, _descode);
                                    
                                    byte_cmdBuff = Encoding.Unicode.GetBytes(encode);
                                    if (!_clientName.Equals(obj))
                                    {
                                        _clientSkt.Send(byte_cmdBuff);
                                    }
                                }
                                break;
                            }
                        default:
                            {
                                //以上都不是则表明第一个包是接收方用户名，继续接受第二个消息正文数据包
                                /*string _receiver = Encoding.Unicode.GetString(_cmdBuff).TrimEnd('\0');
                                byte[] _packetBuff = new byte[_maxPacket];
                                clientSkt.Receive(_packetBuff);*/
                                
                                string _receiver;
                                string _rectmp = Encoding.Unicode.GetString(_cmdBuff).TrimEnd('\0');
                                _descode = _code_tb[obj] as string;
                                _receiver = DecryptDES(_rectmp, _descode);

                                string svrlog = string.Format("[消息日志] {0} ：用户 {1} 向用户 {2} 发送消息。\r\n", DateTime.Now, obj, _receiver);
                                Console.WriteLine(svrlog);
                                byte[] _packetBuff = new byte[_maxPacket];
                                clientSkt.Receive(_packetBuff);

                                string _strMsgBuff = Encoding.Unicode.GetString(_packetBuff).TrimEnd('\0');
                                decode = DecryptDES(_strMsgBuff, _descode);

                                if (_transmit_tb.ContainsKey(_receiver))
                                {
                                    //通过转发表查找接收方的套接字
                                    Socket receiverSkt = _transmit_tb[_receiver] as Socket;
                                    _descode = _code_tb[_receiver] as string;

                                    encode = EncryptDES(decode, _descode);
                                    byte_cmdBuff = Encoding.Unicode.GetBytes(encode);

                                    receiverSkt.Send(byte_cmdBuff);
                                }
                                else
                                {
                                    string sysMessage = string.Format("[系统消息]您刚才的内容没有发送成功。\r\n可能原因：用户 {0} 已离线或者网络阻塞。\r\n\r\n", _receiver);
                                    svrlog = string.Format("[系统消息]{0}：用户 {1} 消息没有发送成功。\r\n可能原因：用户 {2} 已离线或者网络阻塞。\r\n", DateTime.Now, obj, _receiver);
                                    Console.WriteLine(svrlog);
                                    clientSkt.Send(Encoding.Unicode.GetBytes(sysMessage));
                                }
                                break;
                            }
                    }
                }
                catch (SocketException)
                {
                    _transmit_tb.Remove(obj);
                    _code_tb.Remove(obj);
                    string svrlog = string.Format("[系统消息]用户 {0} 的客户端在 {1} 意外终止！当前在线人数：{2}\r\n\r\n", obj, DateTime.Now, _transmit_tb.Count);
                    Console.WriteLine(svrlog);
                    //向所有客户机发送系统消息
                    foreach (DictionaryEntry de in _transmit_tb)
                    {
                        string _clientName = de.Key as string;
                        Socket _clientSkt = de.Value as Socket;
                        _clientSkt.Send(Encoding.Unicode.GetBytes(svrlog));
                    }
                    Console.WriteLine();
                    Thread.CurrentThread.Abort();
                }
            }
        }
        private byte[] RSA_Encode(string rsaencode)
        {

            try
            {

                crypt = new RSACryptoServiceProvider();
                UTF8Encoding enc = new UTF8Encoding();
                bytes = Encoding.UTF8.GetBytes(rsaencode);
                crypt.FromXmlString(readpublickey);
                bytes = crypt.Encrypt(bytes, false);
                string encryttext = enc.GetString(bytes);
                //textBox2.Text = "加密结果：\n" + encryttext + "\n";
                return bytes;
            }
            catch
            {
                Console.WriteLine("请检查是否打开公匙或者公匙是否损坏！");
                return bytes;
            }
        }

        private string RSA_Decode(byte[] rsadecode)
        {

            try
            {
                UTF8Encoding enc = new UTF8Encoding();
                byte[] decryptbyte;

                crypt.FromXmlString(readprivatekey);


                decryptbyte = crypt.Decrypt(rsadecode, false);
                string decrypttext = enc.GetString(decryptbyte);
                return decrypttext;
                //textBox3.Text = "解密结果：\n" + decrypttext + "\n";
            }
            catch
            {
                Console.WriteLine("请检查是否打开私匙或者私匙是否损坏！");
                return Encoding.UTF8.GetString(rsadecode);

            }
        }
        public　static　string　ToHex(byte[]　bytes)
        {
            var　sb　=　new　StringBuilder();
            foreach (byte b in bytes)
            {
                sb.Append(b.ToString("X2"));
                sb.Append(' ');
            }
            return　sb.ToString();
　      }

        /// <summary>
        /// 启动监听，轮询监听客户机请求并将客户端套接字存入转发表
        /// </summary>
        public void StartUp()
        {
            IPAddress _ip = Dns.GetHostAddresses(Dns.GetHostName())[1];//可能有多个,此时的IP是本地IP
            _tcpl = new TcpListener(_ip, _port);
            _tcpl.Start();//开始侦听传入的连接请求。 
            Console.WriteLine("服务器已启动，正在监听...\n");
            Console.WriteLine(string.Format("服务器IP：{0}\t端口号：{1}\n", _ip, _port));

            byte[] bytetmp = new byte[1024];//为什么这里要初始化加密解密？才能让接下来收到的数据解密成功？
            bytetmp = RSA_Encode("vcrm");
            RSA_Decode(bytetmp);
           
            while (true)
            {
                byte[] packetBuff = new byte[_maxPacket];// 接收数据缓冲区大小64K 
                byte[] codeBuff=new byte[128];//des加密密钥缓冲区
                string miyue;
                //AcceptSocket 是一个阻止方法，该方法返回可用于发送和接收数据的 Socket。
                //如果希望避免阻止，请使用 Pending 方法来确定传入连接队列中的连接请求是否可用。 
                //返回的 Socket 是使用远程主机的 IP 地址和端口号初始化的。
                //您可以使用 Socket 类中任何可用的 Send 和 Receive 方法与远程主机进行通信。
                //------------使用 Socket 后，请确保调用其 Close 方法。如果应用程序相对简单，-----------
                //请考虑使用 AcceptTcpClient 方法代替 AcceptSocket 方法。
                //TcpClient 提供了一些通过网络在阻止同步模式下发送和接收数据的简单方法。

                Socket newClient = _tcpl.AcceptSocket();//本地TcpListener 接受新的请求 
                //返回的 Socket 是使用远程主机的 IP 地址和端口号初始化的

                newClient.Receive(packetBuff);//将接受的远程SOCKET,将数据存入接受缓冲区
                string userName = Encoding.Unicode.GetString(packetBuff).TrimEnd('\0');

                //验证是否为唯一用户,有了就发送失败命令给请求客户
                if (_transmit_tb.Count != 0 && _transmit_tb.ContainsKey(userName))
                {
                    newClient.Send(Encoding.Unicode.GetBytes("cmd::Failed"));
                    continue;
                }
                else
                {
                    newClient.Send(Encoding.Unicode.GetBytes("cmd::Successful"));
                }

                //将新连接加入转发表并创建线程为其服务
                _transmit_tb.Add(userName, newClient);
                


                //vcrm
                newClient.Receive(codeBuff);
                Console.WriteLine("经RSA公钥加密后的密文为(用16进制表示)："+ '\n'+ ToHex(codeBuff)+'\r'+'\n');
                miyue = RSA_Decode(codeBuff);
                Console.WriteLine("经RSA密钥解密后的 DES需要的8位密钥为：" + '\n'+ miyue + '\r' + '\n');
                _code_tb.Add(userName, miyue);

               // _transmit_tb.Add("miyue1","12345678");
               // _transmit_tb.Add("miyue2", "abcdefgh");
               // Console.WriteLine("1   :" + _transmit_tb[userName]);
               // Console.WriteLine("2   :" + _transmit_tb["miyue2"]);
                //-----------------------
                string svrlog = string.Format("[系统消息]新用户 {0} 在 {1} 已连接... 当前在线人数: {2}\r\n", userName, DateTime.Now, _transmit_tb.Count);
                Console.WriteLine(svrlog);
                //------------------------

                Thread clientThread = new Thread(new ParameterizedThreadStart(ThreadFunc));
                clientThread.Start(userName);

                //向所有客户机发送系统消息,说有人上线了
                foreach (DictionaryEntry de in _transmit_tb)
                {
                    string _clientName = de.Key as string;
                    Socket _clientSkt = de.Value as Socket;
                    if (!_clientName.Equals(userName))
                    {
                        _clientSkt.Send(Encoding.Unicode.GetBytes(svrlog));
                    }
                }
            }
        }

        #endregion
    }
}